package org.abc.fund.service.dataSourceCollection;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.abc.fund.entity.funds.FundAnnouncement;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Component
public class FundAnnouncementCrawler {
    private final HttpClient client = HttpClient.newHttpClient();

    // 基金代码标准化：去掉.后缀
    private String normalizeFundCode(String code) {
        if (code == null) return null;
        return code.replace(".OF", "")
                .replace(".XSHE", "")
                .replace(".XSHG", "")
                .replace(".", "")
                .trim();
    }

    // 主方法：获取基金公告（默认获取最新一年的公告）
    public List<FundAnnouncement> fetchFundAnnouncements(String fundCodeRaw) {
        return fetchFundAnnouncements(fundCodeRaw, LocalDate.now().minusYears(1));
    }

    // 重载方法：可指定时间范围
    public List<FundAnnouncement> fetchFundAnnouncements(String fundCodeRaw, LocalDate sinceDate) {
        List<FundAnnouncement> result = new ArrayList<>();
        String pureCode = normalizeFundCode(fundCodeRaw);
        if (pureCode == null || pureCode.isEmpty()) {
            return result;
        }
        log.info("基金 {} 获取 {} 之后的公告", pureCode, sinceDate);
        int pageIndex = 1;
        boolean shouldContinue = true;
        while (shouldContinue) {
            JSONArray arr = fetchAnnouncementListPage(pureCode, pageIndex);
            if (arr == null || arr.isEmpty()) {
                break;
            }
            for (int i = 0; i < arr.size(); i++) {
                JSONObject item = arr.getJSONObject(i);
                String annId = item.getString("ID");
                String title = item.getString("TITLE");
                String dateStr = item.getString("PUBLISHDATEDesc");
                if (dateStr == null || dateStr.isEmpty()) {
                    String raw = item.getString("PUBLISHDATE");
                    if (raw != null && raw.length() >= 10) {
                        dateStr = raw.substring(0, 10);
                    }
                }
                LocalDate publishDate = parseDate(dateStr);
                // 检查公告日期是否在指定时间范围内
                if (publishDate == null || publishDate.isBefore(sinceDate)) {
                    log.debug("基金 {} 公告日期 {} 早于 {}，停止获取后续公告", pureCode, publishDate, sinceDate);
                    shouldContinue = false;
                    break;
                }
                String category = classifyAnnouncement(title);
                String content = fetchAnnouncementContent(pureCode, annId);
                FundAnnouncement ann = new FundAnnouncement();
                ann.setFundCode(pureCode);
                ann.setTitle(title);
                ann.setPublishDate(publishDate);
                ann.setSourceUrl("https://fund.eastmoney.com/gonggao/" + pureCode + "," + annId + ".html");
                ann.setContent(content);
                ann.setCategory(category);
                result.add(ann);
                log.debug("基金 {} 添加公告: {} - {}", pureCode, publishDate, title);
            }
            pageIndex++;
        }
        log.info("基金 {} 获取到 {} 条 {} 之后的公告", pureCode, result.size(), sinceDate);
        return result;
    }

    // 其他方法保持不变...
    public String testSingleAnnouncement(String fundCodeRaw, String annId) {
        String pureCode = normalizeFundCode(fundCodeRaw);
        log.info("测试获取公告内容 - 基金: {}, 公告ID: {}", pureCode, annId);
        return fetchAnnouncementContent(pureCode, annId);
    }

    // 分页获取公告列表
    private JSONArray fetchAnnouncementListPage(String pureCode, int pageIndex) {
        try {
            String url = "https://api.fund.eastmoney.com/f10/JJGG?callback=jQuery1830"
                    + "&fundcode=" + pureCode
                    + "&pageIndex=" + pageIndex
                    + "&pageSize=50&type=3";
            HttpRequest req = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("User-Agent", "Mozilla/5.0")
                    .header("Referer", "https://fundf10.eastmoney.com/")
                    .build();
            HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
            String body = resp.body();
            String json = body;
            int firstParen = json.indexOf('(');
            if (firstParen != -1) {
                json = json.substring(firstParen + 1);
            }
            int lastParen = json.lastIndexOf(')');
            if (lastParen != -1) {
                json = json.substring(0, lastParen);
            }
            JSONObject root = JSON.parseObject(json);
            JSONArray data = root.getJSONArray("Data");
            if (data == null) {
                return new JSONArray();
            }
            return data;
        } catch (Exception e) {
            log.warn("公告列表抓取失败：fundCode={}, pageIndex={}, error={}",
                    pureCode, pageIndex, e.getMessage());
            return new JSONArray();
        }
    }

    // 获取公告全文
    private String fetchAnnouncementContent(String fundCode, String annId) {
        try {
            String url = "https://np-cnotice-fund.eastmoney.com/api/content/ann"
                    + "?client_source=web_fund&show_all=1&art_code=" + annId;
            String refererUrl = "https://fund.eastmoney.com/gonggao/"
                    + fundCode + "," + annId + ".html";
            HttpRequest req = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
                    .header("Referer", refererUrl)
                    .header("Accept", "application/json, text/plain, */*")
                    .header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
                    .build();
            HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
            log.info("公告内容API响应 - annId: {}, 状态码: {}", annId, resp.statusCode());
            if (resp.statusCode() != 200) {
                log.warn("公告内容API返回非200状态码: {}", resp.statusCode());
                return "";
            }
            JSONObject root = JSON.parseObject(resp.body());
            JSONObject data = root.getJSONObject("data");
            if (data != null) {
                String content = data.getString("notice_content");
                if (content != null && !content.trim().isEmpty()) {
                    String cleanedContent = cleanInvalidChars(content);
                    log.info("成功获取公告内容，长度: {} 字符", content.length());
                    return cleanedContent;
                }
            }
            String[] possibleFields = {"notice_content", "content", "CONTENT", "art_content", "noticeContent"};
            for (String field : possibleFields) {
                if (data != null) {
                    String content = data.getString(field);
                    if (content != null && !content.trim().isEmpty()) {
                        log.info("通过备用字段 '{}' 获取公告内容，长度: {} 字符", field, content.length());
                        return content;
                    }
                }
            }
            log.warn("公告内容为空 - annId: {}, 完整响应: {}", annId, root.toJSONString());
            return "";
        } catch (Exception e) {
            log.error("公告全文抓取失败：annId={}, error={}", annId, e.getMessage(), e);
            return "";
        }
    }

    // 清理字符串中的非法字符
    private String cleanInvalidChars(String content) {
        if (content == null) {
            return "";
        }
        String cleaned = content.replace("\0", "");
        cleaned = cleaned.replaceAll("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]", "");
        cleaned = cleaned.replaceAll("[\\p{Cntrl}&&[^\r\n\t]]", "");
        try {
            byte[] bytes = cleaned.getBytes(StandardCharsets.UTF_8);
            cleaned = new String(bytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.warn("UTF-8编码清理失败，使用原始内容");
        }
        return cleaned;
    }

    // 公告分类
    public String classifyAnnouncement(String rawTitle) {
        if (rawTitle == null) return "其他";
        String title = rawTitle.replaceAll("\\s+", "");
        if (title.contains("分红") || title.contains("收益分配")
                || title.contains("派息") || title.contains("红利")) {
            return "分红公告";
        }
        if (title.contains("季度报告") || title.contains("季报")) {
            return "季度报告";
        }
        if (title.contains("半年度报告") || title.contains("半年报")) {
            return "半年度报告";
        }
        if (title.contains("年度报告") || title.contains("年报")) {
            return "年度报告";
        }
        if (title.contains("基金合同") || title.contains("招募说明书")) {
            return "法律文件";
        }
        if (title.contains("托管人") && title.contains("变更")
                || (title.contains("管理人") && title.contains("变更"))) {
            return "托管人/管理人变更";
        }
        if (title.contains("基金经理") && title.contains("变更")) {
            return "基金经理变更";
        }
        if (title.contains("暂停") && title.contains("申购")) {
            return "暂停申购";
        }
        if (title.contains("恢复") && title.contains("申购")) {
            return "恢复申购";
        }
        if (title.contains("暂停") && title.contains("赎回")) {
            return "暂停赎回";
        }
        if (title.contains("恢复") && title.contains("赎回")) {
            return "恢复赎回";
        }
        if (title.contains("份额折算") || title.contains("折算")) {
            return "份额折算";
        }
        return "其他";
    }

    // 日期解析
    private LocalDate parseDate(String dateStr) {
        if (dateStr == null || dateStr.isEmpty()) {
            return null;
        }
        try {
            if (dateStr.contains("-")) {
                return LocalDate.parse(dateStr.substring(0, 10));
            }
            if (dateStr.length() == 8) {
                return LocalDate.parse(dateStr, DateTimeFormatter.BASIC_ISO_DATE);
            }
        } catch (Exception e) {
            log.warn("公告日期解析失败：raw={}, error={}", dateStr, e.getMessage());
        }
        return null;
    }
}